import { cleanup, effect } from './effect/effect.js'

export function watch(source, cb, options) {
  // 1. 参数归一化处理，这里排除数组的情况
  let getter
  if (typeof source === 'function') {
    getter = source
  } else {
    getter = () => traverse(source)
  }

  // 用于存储 getter 上一次的值和当前值
  let oldValue, newValue
  const job = () => {
    newValue = effectFn()
    cb(newValue, oldValue)
    oldValue = newValue
  }

  // 依赖收集
  const effectFn = effect(() => getter(), {
    lazy: true,
    shcheduler: () => {
      if (options.flush === 'post') {
        Promise.resolve().then(job)
      } else {
        job()
      }
    }
  })

  if (options.immediate) {
    job()
  } else {
    oldValue = effectFn()
  }

  return () => {
    cleanup(effectFn)
  }
}

/**
 * 该工具方法用于遍历对象的所有属性，包括嵌套对象的属性
 * 之所以要遍历，是为了触发这些属性的依赖收集
 * @param {*} value
 * @param {*} set
 */
function traverse(value, seen = new Set()) {
  if (typeof value !== 'object' || value === null || seen.has(value)) {
    return value
  }

  // 将当前的 value 添加到 seen 集合中，标记为已经访问过了，防止循环引用导致无限递归
  seen.add(value)

  // 使用 for...in... 循环遍历对象的所有属性
  for (const key in value) {
    // 递归调用 traverse，传入当前属性的值和 seen 集合
    traverse(value[key], seen)
  }

  // 返回原始值
  return value
}
